﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace FourSquareAPI
{
    public class FourSquare
    {
        private string USER_AGENT = "theBrent-FourSquare.NET:" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
        private string _Username;
        private string _Password;
        private NetworkCredential c;

        public FourSquare(string Username, string Password)
        {
            this._Username = Username;
            this._Password = Password;
            this.c = new NetworkCredential(this._Username, this._Password);
        }

        private XmlDocument GetData(Uri Url)
        {
            Uri checkin_uri = new Uri("http://api.foursquare.com/v1/checkins");
            HttpWebRequest r = (HttpWebRequest)HttpWebRequest.Create(checkin_uri);
            r.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(this._Username + ":" + this._Password)));
            r.UserAgent = USER_AGENT;
            using (WebResponse wr = r.GetResponse())
            {
                using (Stream s = wr.GetResponseStream())
                {
                    XmlDocument d = new XmlDocument();
                    d.Load(wr.GetResponseStream());
                    return d;
                }
            }
        }

        private XmlDocument PostData(Uri Url, Dictionary<string, string> Data)
        {
            HttpWebRequest r = (HttpWebRequest)HttpWebRequest.Create(Url);
            r.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(this._Username + ":" + this._Password)));
            r.Method = "POST";
            r.ContentType = "application/x-www-form-urlencoded";
            string content = "?";
            foreach (string k in Data.Keys)
            {
                content = k + "=" + Data[k] + "&";
            }
            content = content.TrimEnd(new char[]{'&'});   
            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] byte1 = encoding.GetBytes(content);
            r.ContentLength = byte1.Length;
            using (Stream rs = r.GetRequestStream())
            {
                rs.Write(byte1, 0, byte1.Length);
                rs.Close();
                using (WebResponse wr = r.GetResponse())
                {
                    using (Stream s = wr.GetResponseStream())
                    {
                        XmlDocument d = new XmlDocument();
                        d.Load(s);
                        return d;
                    }
                }
            }
        }


        /* Checkins
            URL: http://api.foursquare.com/v1/checkins
            HTTP Method(s): GET
            Requires Authentication: Yes
            Parameters:
                geolat - (optional, but recommended)
                geolong - (optional, but recommended)
        */
        public List<checkin> GetCheckins()
        {
            List<checkin> checkins = new List<checkin>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/checkins"));
            XmlNodeList nlist = d.SelectNodes("/checkins/checkin");
            foreach(XmlNode n in nlist)
            {
                checkins.Add(new checkin(n));
            }
            return checkins;
        }
        public List<checkin> GetCheckins(double Latitude, double Longitude)
        {
            List<checkin> checkins = new List<checkin>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/checkins?geolat=" + Latitude.ToString() + "&geolong=" + Longitude.ToString()));
            XmlNodeList nlist = d.SelectNodes("/checkins/checkin");
            foreach (XmlNode n in nlist)
            {
                checkins.Add(new checkin(n));
            }
            return checkins;
        }

        /* Checkin
            URL: http://api.foursquare.com/v1/checkin
            HTTP Method(s): POST
            Requires Authentication: Yes
            Parameters:
                vid - (optional, not necessary if you are 'shouting' or have a venue name). ID of the venue where you want to check-in
                venue - (optional, not necessary if you are 'shouting' or have a vid) if you don't have a venue ID or would rather prefer a 'venueless' checkin, pass the venue name as a string using this parameter. it will become an 'orphan' (no address or venueid but with geolat, geolong)
                shout - (optional) a message about your check-in. the maximum length of this field is 140 characters
                private - (optional). "1" means "don't show your friends". "0" means "show everyone"
                twitter - (optional, defaults to the user's setting). "1" means "send to Twitter". "0" means "don't send to Twitter"
                facebook - (optional, defaults to the user's setting). "1" means "send to Facebook". "0" means "don't send to Facebook"
                geolat - (optional, but recommended)
                geolong - (optional, but recommended)
        */
        
        public checkin CheckinTo(string vid, string venue, bool? Private, string shout, bool? twitter, bool? facebook, double? geolat, double? geolong)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            if (vid != null)
            {
                parameters.Add("vid", vid);
            }
            if (venue != null)
            {
                parameters.Add("venue", venue);
            }
            if (Private.HasValue)
            {
                if (Private.Value)
                {
                    parameters.Add("private", "1");
                }
                else
                {
                    parameters.Add("private", "0");
                }
            }
            if (shout != null)
            {
                parameters.Add("shout",shout);
            }
            if (twitter.HasValue)
            {
                if (twitter.Value)
                {
                    parameters.Add("twitter","1");
                }
                else
                {
                    parameters.Add("twitter","0");
                }
            }
            if (facebook.HasValue)
            {
                if (facebook.Value)
                {
                    parameters.Add("facebook","1");
                }
                else
                {
                    parameters.Add("facebook","0");
                }
            }
            if (geolat.HasValue && geolong.HasValue)
            {
                parameters.Add("geolat",geolat.Value.ToString());
                parameters.Add("geolong",geolong.Value.ToString());
            }
            XmlDocument d = PostData(new Uri("http://api.foursquare.com/v1/checkin"), parameters);
            checkin c = new checkin(d.SelectSingleNode("/checkin"));
            return c;
        }

        /* History
            URL: http://api.foursquare.com/v1/history
            HTTP Method(s): GET
            Requires Authentication: Yes
            Parameters:
                l - limit of results (optional, default: 20). number of checkins to return
                sinceid - id to start returning results from (optional, if omitted returns most recent results)
        */
        public List<checkin> History()
        {
            List<checkin> checkins = new List<checkin>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/history"));
            XmlNodeList nlist = d.SelectNodes("/checkins/checkin");
            foreach (XmlNode n in nlist)
            {
                checkins.Add(new checkin(n));
            }
            return checkins;
        }
        
        /* User Detail
            URL: http://api.foursquare.com/v1/user
            HTTP Method(s): GET
            Requires Authentication: Yes
            Parameters:
                uid - userid for the user whose information you want to retrieve. if you do not specify a 'uid', the authenticated user's profile data will be returned.
                badges - (optional, default: false) set to true ("1") to also show badges for this user. by default, this will only show badges from the authenticated user's current city.
                mayor - (optional, default: false) set to true ("1") to also show venues for which this user is a mayor. by default, this will show mayorships worldwide.
        */
        public user User(string Uid)
        {
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/user?uid=" + Uid));
            return new user(d.SelectSingleNode("/user"));
        }

        /* Friends
            URL: http://api.foursquare.com/v1/friends
            HTTP Method(s): GET
            Requires Authentication: Yes
            Parameters:
                uid - user id of the person for whom you want to pull a friend graph (optional)
        */
        public List<user> Friends()
        {
            List<user> u = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/friends"));
            XmlNodeList nlist = d.SelectNodes("/friends/user");
            foreach (XmlNode n in nlist)
            {
                u.Add(new user(n));
            }
            return u;
        }
        public List<user> Friends(string Uid)
        {
            List<user> u = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/friends?uid=" + Uid));
            XmlNodeList nlist = d.SelectNodes("/friends/user");
            foreach (XmlNode n in nlist)
            {
                u.Add(new user(n));
            }
            return u;
        }

        /* Nearby and Search
            URL: http://api.foursquare.com/v1/venues
            HTTP Method(s): GET
            Requires Authentication: Recommended
            Parameters:
                geolat - latitude (required)
                geolong - longitude (required)
                l - limit of results (optional, default 10)
                q - keyword search (optional)
        */
        public List<venue> SearchVenues(double geolat, double geolong, string q)
        {
            List<venue> venues = new List<venue>();
            //XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/venues?geolat=38.895111&geolong=-77.036667&q=" + q));
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/venues?geolat=" + geolat.ToString() + "&geolong=" + geolong.ToString() + "&q=" + q));
            XmlNodeList nlist = d.SelectNodes("/venues/group/venue");
            foreach (XmlNode n in nlist)
            {
                venues.Add(new venue(n));
            }
            return venues;
        }

        /* Venue Details
                URL: http://api.foursquare.com/v1/venue
                HTTP Method(s): GET
                Requires Authentication: Recommended
                Parameters:
                    vid - the ID for the venue for which you want information
        */
        public venue VenueDetails(string vid)
        {
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/venue?vid=" + vid));
            return new venue(d.SelectSingleNode("/venue"));
        }

        /* Add venue
                URL: http://api.foursquare.com/v1/addvenue
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    name - the name of the venue
                    address - the address of the venue (e.g., "202 1st Avenue")
                    crossstreet - the cross streets (e.g., "btw Grand & Broome")
                    city - the city name where this venue is
                    state - the state where the city is
                    zip - (optional) the ZIP code for the venue
                    phone - (optional) the phone number for the venue
                    geolat - (optional, but recommended)
                    geolong - (optional, but recommended)
        */

        /* Propose Venue Edit
                URL: http://api.foursquare.com/v1/venue/proposeedit
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    vid - (required) the venue for which you want to propose an edit
                    name - (required) the name of the venue
                    address - (required) the address of the venue (e.g., "202 1st Avenue")
                    crossstreet - the cross streets (e.g., "btw Grand & Broome")
                    city - (required) the city name where this venue is
                    state - (required) the state where the city is
                    zip - (optional) the ZIP code for the venue
                    phone - (optional) the phone number for the venue
                    geolat - (required)
                    geolong - (required)
        */

        /* Flag venue as closed
                URL: http://api.foursquare.com/v1/venue/flagclosed
                Formats: XML, JSON
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    vid - the venue that you want marked closed (required)
        */

        /* Nearby Tips
                URL: http://api.foursquare.com/v1/tips
                HTTP Method(s): GET
                Requires Authentication: Recommended
                Parameters:
                    geolat - latitude (required)
                    geolong - longitude (required)
                    l - limit of results (optional, default 30)
        */
        public List<tip> GetTips(double geolat, double geolong)
        {
            List<tip> tips = new List<tip>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/tips?geolat=" + geolat.ToString() + "&geolong=" + geolong.ToString()));
            XmlNodeList nlist = d.SelectNodes("/tips/group/tip");
            foreach (XmlNode n in nlist)
            {
                tips.Add(new tip(n));
            }
            return tips;
        }

        /* Add tip/to-do
                URL: http://api.foursquare.com/v1/addtip
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    vid - the venue where you want to add this tip (required)
                    text - the text of the tip or to-do item (required)
                    type - specify one of 'tip' or 'todo' (optional, default: tip)
                    geolat - (optional, but recommended)
                    geolong - (optional, but recommended)
        */

        /* Mark tip as to-do
                URL: http://api.foursquare.com/v1/tip/marktodo
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    tid - the tip that you want to mark to-do (required)
        */

        /* Mark tip as done
                URL: http://api.foursquare.com/v1/tip/markdone
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    tid - the tip that you want to mark done (required)
        */

        /* Pending friend requests
                URL: http://api.foursquare.com/v1/friend/requests
                HTTP Method(s): GET
                Requires Authentication: Yes
        */
        public List<user> GetFriendRequests()
        {
            List<user> requests = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/friend/requests"));
            XmlNodeList nlist = d.SelectNodes("/requests/user");
            foreach (XmlNode n in nlist)
            {
                requests.Add(new user(n));
            }
            return requests;
        }

        /* Approve friend request
                URL: http://api.foursquare.com/v1/friend/approve
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    uid - the user ID of the user who you want to approve
        */

        /* Deny friend request
                URL: http://api.foursquare.com/v1/friend/deny
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    uid - the user ID of the user who you want to deny
        */

        /* Send a friend request
                URL: http://api.foursquare.com/v1/friend/sendrequest
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    uid - the user ID of the user to whom you want to send a friend request
        */
    
        /* Find friends by name
                URL: http://api.foursquare.com/v1/findfriends/byname
                HTTP Method(s): GET
                Requires Authentication: Yes
                Parameters:
                    q - the string you want to use to search firstnames and lastnames
        */
        public List<user> SearchUsers(string q)
        {
            List<user> users = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/findfriends/byname?q=" + q));
            XmlNodeList nlist = d.SelectNodes("/users/user");
            foreach (XmlNode n in nlist)
            {
                users.Add(new user(n));
            }
            return users;
        }

        /* Find friends by phone
                URL: http://api.foursquare.com/v1/findfriends/byphone
                HTTP Method(s): GET
                Requires Authentication: Yes
                Parameters:
                    q - the string you want to use to search for phone numbers
        */
        public List<user> SearchUsersByPhone(string q)
        {
            List<user> users = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/findfriends/byphone?q=" + q));
            XmlNodeList nlist = d.SelectNodes("/users/user");
            foreach (XmlNode n in nlist)
            {
                users.Add(new user(n));
            }
            return users;
        }

        /* Find friends by using a Twitter name
                URL: http://api.foursquare.com/v1/findfriends/bytwitter
                HTTP Method(s): GET
                Requires Authentication: Yes
                Parameters:
                    q - (optional) the Twitter name you want to use to search
        */
        public List<user> SearchUsersByTwitter(string q)
        {
            List<user> users = new List<user>();
            XmlDocument d = GetData(new Uri("http://api.foursquare.com/v1/findfriends/bytwitter?q=" + q));
            XmlNodeList nlist = d.SelectNodes("/users/user");
            foreach (XmlNode n in nlist)
            {
                users.Add(new user(n));
            }
            return users;
        }

        /* Set pings
                URL: http://api.foursquare.com/v1/settings/setpings
                HTTP Method(s): POST
                Requires Authentication: Yes
                Parameters:
                    self - the ping status for yourself (globally). possible values are on, off and goodnight.
                    [uid] - set the ping status for a friend. possible values are on and off.
        */
    }
}
